#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <memory>
const int defaultconnectnum = 5;
const int defaultsockfd = -1;
const int defaultconnecttime = 1;
enum class Status // C++11 强类型枚举 会进行类型转换
{
    NEW,          // 新建状态，就是单纯的连接
    CONNECTING,   // 正在连接，仅仅方便查询conn状态
    CONNECTED,    // 连接或者重连 成功
    DISCONNECTED, // 首次连接失败 进入重新连接
    CLOSED        // 连接失败，经历重连，无法连接
};
class Connetion
{
public:
    Connetion(std::string ip, uint16_t port)
        : _status(Status::NEW),
          _ip(ip),
          _port(port),
          _sockfd(defaultsockfd),
          _connect_num(defaultconnectnum),
          _connet_time(defaultconnecttime)
    {
    }
    Status get_status()
    {
        return _status;
    }
    void Connect()
    {
        int n = socket(AF_INET, SOCK_STREAM, 0);
        if (n < 0)
        {
            std::cerr << "socker failsure!!!" << std::endl;
            exit(1);
        }
        _sockfd = n;
        // 客户端我们不用进行我们的显示bind 下面调用connect
        struct sockaddr_in serve;
        memset(&serve, 0, sizeof(serve));
        serve.sin_family = AF_INET;
        serve.sin_port = htons(_port);

        // 相对与 iner_aton线程安全
        inet_pton(AF_INET, _ip.c_str(), &serve.sin_addr);

        n = connect(_sockfd, (sockaddr *)&serve, sizeof(serve));
        if (n < 0)
        {
            // 连接失败
            // 此时我们把我们的sockfd关闭
            Close();
            _status = Status::DISCONNECTED;
        }
        else
        {
            _status = Status::CONNECTED;
        }
    }
    void ReConnect()
    {
        int count = 0;
        while (true)
        {
            _status = Status::CONNECTING;
            Connect();
            count++;
            if (_status == Status::CONNECTED)
            {
                std::cout << "重连成功！！！" << std::endl;
                break;
            }
            if (count > _connect_num)
            {
                _status = Status::CLOSED;
                break;
            }
            std::cout << "第" << count << "次重新连接！！！" << std::endl;
            sleep(_connet_time);
        }
    }
    // 连接成功进入通信模块
    void Process()
    {
        // 简单的IO即可
        while (true)
        {
            std::string inbuffer;
            std::cout << "Please Enter# ";
            getline(std::cin, inbuffer);
            if (inbuffer.empty())
                continue;
            ssize_t n = write(_sockfd, inbuffer.c_str(), inbuffer.size());
            if (n > 0)
            {
                char buffer[1024];
                ssize_t m = read(_sockfd, buffer, sizeof(buffer) - 1);
                if (m > 0)
                {
                    buffer[m] = 0;
                    std::cout << "echo messsge -> " << buffer << std::endl;
                }
                else if (m == 0) // 这里证明server端掉线了
                {
                    _status = Status::DISCONNECTED;
                    break;
                }
                else
                {
                    std::cout << "read m : " << m << "errno: " << errno << "errno string: " << strerror(errno) << std::endl;
                    _status = Status::CLOSED;
                    break;
                }
            }
            else
            {
                std::cout << "write n : " << n << "errno: " << errno << "errno string: " << strerror(errno) << std::endl;
                _status = Status::CLOSED;
                break;
            }
        }
    }
    void Close()
    {
        if (_sockfd != -1)
        {
            close(_sockfd);
            _status = Status::CLOSED;
            _sockfd = -1;
        }
    }

private:
    Status _status;
    std::string _ip;
    uint16_t _port;
    int _sockfd;
    int _connect_num;
    int _connet_time;
};
class TcpClient
{
public:
    TcpClient(std::string ip, uint16_t port) : _ip(ip),
                                               _port(port),
                                               _conn(_ip, _port)
    {
    }
    void Excute()
    {
        while (true)
        {
            switch (_conn.get_status())
            {
            case Status::NEW:
                _conn.Connect();
                break;
            case Status::DISCONNECTED:
                _conn.ReConnect();
                break;
            case Status::CONNECTED:
                _conn.Process();
                break;
            case Status::CLOSED:
                _conn.Close();
                std::cout << "重连失败, 退出." << std::endl;
                return;
            default:
                break;
            }
            sleep(1);
        }
    }
    ~TcpClient()
    {
    }

private:
    std::string _ip;
    uint16_t _port;
    Connetion _conn;
};
void Usage(const std::string &process)
{
    std::cout << "Usage: " << process << " server_ip server_port" << std::endl;
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
    }
    uint16_t port = std::stoi(argv[2]);
    std::unique_ptr<TcpClient> client = std::make_unique<TcpClient>(argv[1], port);
    client->Excute();
    return 0;
}